﻿using System;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using System.IO;
using System.Collections.Generic;
using System.Diagnostics;

/// <summary>
/// Manages Screen Savers.
/// This class cannot be inherited.
/// </summary>
public sealed class ScreenSaver
{
    #region PInvoke

    #region Signatures for unmanaged calls
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern bool SystemParametersInfo(int uAction, int uParam, ref int lpvParam, int flags);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern bool SystemParametersInfo(int uAction, int uParam, ref bool lpvParam, int flags);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern int PostMessage(IntPtr hWnd, int wMsg, int wParam, int lParam);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern IntPtr OpenDesktop(string hDesktop, int Flags, bool Inherit, uint DesiredAccess);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern bool CloseDesktop(IntPtr hDesktop);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern bool EnumDesktopWindows(IntPtr hDesktop, EnumDesktopWindowsProc callback, IntPtr lParam);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern bool IsWindowVisible(IntPtr hWnd);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern IntPtr GetForegroundWindow();

    [DllImport("user32.dll", EntryPoint = "GetDesktopWindow")]
    private static extern IntPtr GetDesktopWindow();

    [DllImport("user32.dll")]
    private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);
    #endregion

    #region Callbacks
    private delegate bool EnumDesktopWindowsProc(IntPtr hDesktop, IntPtr lParam);
    private static bool StopScreenSaverFunc(IntPtr hWnd, IntPtr lParam)
    {
        if (IsWindowVisible(hWnd)) PostMessage(hWnd, WM_CLOSE, 0, 0);
        return true;
    }
    #endregion

    #region Constants
    private const int SPI_GETSCREENSAVERACTIVE = 16;
    private const int SPI_SETSCREENSAVERACTIVE = 17;
    private const int SPI_GETSCREENSAVERTIMEOUT = 14;
    private const int SPI_SETSCREENSAVERTIMEOUT = 15;
    private const int SPI_GETSCREENSAVERRUNNING = 114;
    private const int SPIF_SENDWININICHANGE = 2;

    private const uint DESKTOP_WRITEOBJECTS = 0x0080;
    private const uint DESKTOP_READOBJECTS = 0x0001;
    private const int WM_CLOSE = 16;
    #endregion

    #endregion

    #region Objects

    /// <summary>
    /// Used by GetInstalledScreenSavers() to Create ScreenSaver Objects.
    /// </summary>
    /// <param name="path">The Path to the .src file of the screensaver.</param>
    /// <exception cref="System.IO.FileLoadException">File does not exist or is not a Screensaver.</exception>
    ScreenSaver(string path)
    {
        if (!File.Exists(path) || !Path.GetExtension(path).ToLower().Equals(".scr")) throw new FileLoadException("File does not exist or is not a Screensaver.");
        this.Source = path;
    }

    /// <summary>
    /// The Path to the .scr file.
    /// </summary>
    public string Source { get; private set; }

    /// <summary>
    /// Preview the Screensaver.
    /// </summary>
    public void Preview()
    {
        try { Process.Start(Source); }
        catch { }
    }

    /// <summary>
    /// Opens the Configuration Screen.
    /// </summary>
    public void Configure()
    {
        try
        {
            ProcessStartInfo p = new ProcessStartInfo(Source);
            p.Verb = "config";
            Process.Start(p);
        }
        catch { }
    }

    /// <summary>
    /// Installs the Screen Saver.
    /// </summary>
    void Install()
    {
        try
        {
            ProcessStartInfo p = new ProcessStartInfo(Source);
            p.Verb = "install";
            Process.Start(p);
        }
        catch { }
    }

    /// <summary>
    /// Checks whether two ScreenSaver objects refer to the same .scr file.
    /// </summary>
    public static bool operator ==(ScreenSaver S1, ScreenSaver S2) { return S1.Source.Equals(S1.Source); }

    /// <summary>
    /// Checks whether two ScreenSaver objects do not refer to the same .scr file.
    /// </summary>
    public static bool operator !=(ScreenSaver S1, ScreenSaver S2) { return !(S1 == S2); }

    #endregion

    #region Management

    public static ScreenSaver[] GetInstalledScreenSavers()
    {
        FileInfo[] scrFiles = new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.System)).GetFiles("*.scr");
        FileInfo[] scrFiles86 = new FileInfo[0];
        if (Environment.Is64BitOperatingSystem) scrFiles86 = new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.SystemX86)).GetFiles("*.scr");
        List<ScreenSaver> temp = new List<ScreenSaver>();
        foreach (FileInfo scr in scrFiles) temp.Add(new ScreenSaver(scr.FullName));
        foreach (FileInfo scr in scrFiles86) temp.Add(new ScreenSaver(scr.FullName));
        return temp.ToArray();
    }

    public static ScreenSaver[] GetScreenSavers(string folder)
    {
        FileInfo[] scrFiles = new DirectoryInfo(folder).GetFiles("*.scr");
        List<ScreenSaver> temp = new List<ScreenSaver>();
        foreach (FileInfo scr in scrFiles) temp.Add(new ScreenSaver(scr.FullName));
        return temp.ToArray();
    }

    /// <summary>
    /// Gets or Sets whether the Screensaver is Active (Enabled, but not necessarily Running).
    /// </summary>
    public static bool IsActive
    {
        get
        {
            bool isActive = false;
            SystemParametersInfo(SPI_GETSCREENSAVERACTIVE, 0, ref isActive, 0);
            return isActive;
        }
        set
        {
            int nullVar = 0;
            if (value) SystemParametersInfo(SPI_SETSCREENSAVERACTIVE, 1, ref nullVar, SPIF_SENDWININICHANGE);
            else SystemParametersInfo(SPI_SETSCREENSAVERACTIVE, 0, ref nullVar, SPIF_SENDWININICHANGE);
        }
    }

    /// <summary>
    /// Gets or Sets the Screensaver Timeout Setting in Seconds.
    /// </summary>
    public static int Timeout
    {
        get
        {
            int value = 0;
            SystemParametersInfo(SPI_GETSCREENSAVERTIMEOUT, 0, ref value, 0);
            return value;
        }
        set
        {
            int nullVar = 0;
            SystemParametersInfo(SPI_SETSCREENSAVERTIMEOUT, value, ref nullVar, SPIF_SENDWININICHANGE);
        }
    }

    /// <summary>
    /// Gets or Sets whether the screen saver is actually running.
    /// </summary>
    public static bool IsRunning
    {
        get
        {
            bool isRunning = false;
            SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, ref isRunning, 0);
            return isRunning;
        }
        set
        {
            if (value && !IsRunning)
            {
                if (!IsActive) IsActive = true;
                int SC_SCREENSAVE = 0xF140;
                uint WM_SYSCOMMAND = 0x0112;
                SendMessage(GetDesktopWindow(), WM_SYSCOMMAND, SC_SCREENSAVE, 0);
            }
            else if (!value && IsRunning)
            {
                IntPtr hDesktop = OpenDesktop("Screen-saver", 0, false, DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS);
                if (hDesktop != IntPtr.Zero)
                {
                    EnumDesktopWindows(hDesktop, new EnumDesktopWindowsProc(StopScreenSaverFunc), IntPtr.Zero);
                    CloseDesktop(hDesktop);
                }
                else PostMessage(GetForegroundWindow(), WM_CLOSE, 0, 0);
            }
        }
    }

    /// <summary>
    /// Gets the Currently active ScreenSaver Object.
    /// </summary>
    /// <exception cref="System.InvalidOperationException">ScreenSaver is not Active or a valid ScreenSaver has not been assigned.</exception>
    /// <exception cref="System.IO.FileLoadException">Registered File does not exist or is not a Screensaver.</exception>
    public static ScreenSaver Current
    {
        get
        {
            string temp = Registry.CurrentUser.OpenSubKey("Control Panel\\Desktop").GetValue("SCRNSAVE.EXE", String.Empty).ToString();
            if (!File.Exists(temp) || !Path.GetExtension(temp).ToLower().Equals(".scr")) throw new FileLoadException("Registered File does not exist or is not a Screensaver.");
            if (String.IsNullOrWhiteSpace(temp)) throw new InvalidOperationException("ScreenSaver is not Active or a valid ScreenSaver has not been assigned.");
            return new ScreenSaver(temp);
        }
        set
        {
            Registry.CurrentUser.OpenSubKey("Control Panel\\Desktop", true).SetValue("SCRNSAVE.EXE", value.Source, RegistryValueKind.String);
        }
    }

    public override bool Equals(object obj) { return obj is ScreenSaver ? this == (ScreenSaver)obj : false; }

    public override string ToString() { return Source; }

    #endregion
}